package com.gitee.service.openapi;

import com.gitee.gateway.core.common.VerticleBase;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.AllowForwardHeaders;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * HTTP 服务基类
 */
public abstract class HttpVerticleBase extends VerticleBase {

    protected HttpServer server;
    protected static List<String> staticFileExts =
            List.of("js", "css", "html", "htm", "png", "jpg",
                    "gif", "woff2", "woff", "ttf", "svg", "eot", "ico", "swf");

    /**
     * start the http server
     */
    protected Future<HttpServer> startHttpServer(Router router) {
        JsonObject config = config();
        String bind = config.getString("bind");
        int port = config.getInteger("port");
        InetSocketAddress address = StringUtils.isBlank(bind) ? new InetSocketAddress(port) : new InetSocketAddress(bind, port);
        HttpServerOptions options = new HttpServerOptions(config);
        server = vertx.createHttpServer(options);
        return server.requestHandler(router).listen(SocketAddress.inetSocketAddress(address));
    }

    @Override
    public void stop() {
        this.server.close();
    }

    /**
     * global logger
     */
    protected Router buildGlobalHandler(Router router) {
        JsonObject config = config();
        router.allowForward(AllowForwardHeaders.ALL); //通过代理转发的请求，用于获取真实IP

        //cors handler
        Set<HttpMethod> allowedMethods = Set.of(
                HttpMethod.GET,
                HttpMethod.POST,
                HttpMethod.PUT,
                HttpMethod.DELETE,
                HttpMethod.OPTIONS,
                HttpMethod.PATCH
        );
        router.route().handler(CorsHandler.create()
                .addRelativeOrigin(".*")
                .allowCredentials(true)
                .allowedMethods(allowedMethods));
        //x-response-time: 23ms
        router.route().handler(ResponseTimeHandler.create());
        //timeout
        router.route().handler(TimeoutHandler.create(config.getLong("timeout")));
        //HSTS
        router.route().handler(HSTSHandler.create());
        // server info
        router.route().handler(routingContext -> {
            routingContext.addHeadersEndHandler(v -> {
                routingContext.response()
                        .putHeader("server", config.getString("server"))
                        .putHeader("date", new Date().toString());
            });
            routingContext.next();
        });

        if (config.getBoolean("log"))
            router.route().handler(LoggerHandler.create(LoggerFormat.CUSTOM).customFormatter((ctx, ms) -> {
                final MultiMap headers = ctx.request().headers();
                String userAgent = headers.get("user-agent");
                userAgent = userAgent == null ? "-" : userAgent;
                String remoteClient = ctx.request().remoteAddress().host();
                return String.format("%s - \"%s %s %s\" %d %d - %dms - \"%s\"",
                        remoteClient,
                        ctx.request().method(),
                        ctx.request().uri(),
                        ctx.request().version().alpnName().toUpperCase(),
                        ctx.request().response().getStatusCode(),
                        ctx.request().response().bytesWritten(),
                        ms,
                        userAgent);
            }));

        //body handler last
        long bodyLimit = config.getLong("bodyLimit", -1L);
        router.route().handler(BodyHandler.create()
                .setUploadsDirectory("temp")
                .setDeleteUploadedFilesOnEnd(true)
                .setBodyLimit(bodyLimit));

        return router;
    }

    /**
     * 读取 HTTP 服务配置
     *
     * @return JsonObject
     */
    @Override
    public JsonObject config() {
        JsonObject config = super.config();
        //处理 body Limit
        long nBodyLimit = -1;
        String bodyLimit = StringUtils.trim(StringUtils.upperCase(config.getString("bodyLimit")));
        if (bodyLimit != null && bodyLimit.length() > 1) {
            int value = NumberUtils.toInt(bodyLimit.substring(0, bodyLimit.length() - 1), -1);
            nBodyLimit = switch (Character.toUpperCase(bodyLimit.charAt(bodyLimit.length() - 1))) {
                case 'K' -> value * 1024L;
                case 'M' -> value * 1024L * 1024;
                case 'G' -> value * 1024L * 1024 * 1024;
                default -> NumberUtils.toLong(bodyLimit);
            };
            config.put("bodyLimit", (nBodyLimit > 0) ? nBodyLimit : -1);
        }
        return config;
    }

}
